# Using a MarshalledObject to create persistent data

This tutorial should not be the first activation tutorial that you read. This tutorial assumes that you have already read one or more of the three introductory activation tutorials:

- Creating an Activatable Object
- Making a UnicastRemoteObject Activatable
- Activating an object that does not extend java.rmi.activation.Activatable

In the case of a `UnicastRemoteObject`, it is easy to pass command-line arguments to the implementation class, because the server program that received those arguments is always running during the lifetime of the remote object implementation. For activatable objects, however, the setup class may exit immediately after registering the activation descriptor with the RMI daemon and registering the stub with the `rmiregistry`.

The `MarshalledObject` class provides a flexible mechanism for passing persistence or initialization data through the `ActivationDesc`, registered with `rmid`, rather than hard-coding values into the implementation's class file.

Note: For the remainder of this tutorial, the terms "activatable object implementation", "activatable object," and "implementation" may be used interchangeably to refer to the class, `examples.activation.MyPersistentClass`, which implements a remote interface and is activatable.

In this tutorial the setup class, `examples.activation.Setup4`, does two new things:

- It constructs a `java.util.Properties` object to pass the location of the `java.security.properties` file to the constructor of an `ActivationGroupDescriptor`, which in turn, gets passed to the constructor of the `ActivationDesc.`
- It uses the `MarshalledObject`, that it passes to the `ActivationDesc` constructor, to store a `java.io.File` object that represents the location of the persistent data storage.

In this example, if the `persistentObjectStore.ser` file exists, the activatable object implementation is initialized with the persistent data from the file. Otherwise, if the file does not exist, the activatable object initializes itself as though this is the first time a client has tried to send data.

The client program, `examples.activation.Client4`, passes a vector of transaction-like data to the activatable object, and that data is added to the implementation object's vector. Each time a client calls the implementation (to add more transaction data), the activatable implementation stores its state (writes the vector) out to the file specified by the `MarshalledObject`.

This tutorial is organized as follows:

- The steps to create a remote interface
- The steps to create the implementation class
- The steps to create the setup class
- The steps to compile and run the example code

The files needed for this tutorial are:

- `Client4.java`, the class which will invoke a method on an activatable object
- `YetAnotherRemoteInterface.java`, the interface that extends `java.rmi.Remote`, implemented by:
- `MyPersistentClass.java`, the class which is activatable
- `Setup4.java`, the class which registers information about the activatable class with the RMI registry and the RMI daemon

You may notice that while the client code is included, it is not discussed in a step-by-step manner, like the implementation and setup classes. The reason for this omission, is that the client code for activatable objects is no different than the RMI client code for accessing non-activatable remote objects. Activation is strictly a server-side implementation decision.

For all of the source code used in the activation tutorials, you may choose from these formats:

- activation.zip
- activation.tar
- activation.tar.Z

Creating the remote interface

Create an interface that describes each of the methods that you would like to call remotely. For this example, the remote interface will be `examples.activation.YetAnotherRemoteInterface`. There are three steps to create a remote interface:

1. Make the appropriate imports in the interface
2. Extend `java.rmi.Remote`
3. Declare each of the methods that may be called remotely

> Step 1:
> Make the appropriate imports in your interface

>     import java.rmi.*;
>     import java.util.Vector;

> Step 2:
> Extend java.rmi.Remote

>     public interface YetAnotherRemoteInterface extends Remote {

> Step 3:
> Declare each of the methods that may be called remotely

>     public Vector calltheServer(Vector v) throws RemoteException;

Creating the implementation class

For this example, the implementation class will be `examples.activation.MyPersistentClass`. There are five steps to create an activatable implementation class that uses a `MarshalledObject`:

1. Make the appropriate imports in the implementation class
2. Extend your class from`java.rmi.activation.Activatable`
3. Declare a two-argument constructor in the implementation class
4. Write the methods that use the `MarshalledObject`, to save and restore the object's data state
5. Implement the remote interface method(s)

> Step 1:
> Make the appropriate imports in the implementation class

>     import java.io.*;
>     import java.rmi.*;
>     import java.rmi.activation.*;
>     import java.util.Vector;

> Step 2:
> Extend your class from `java.rmi.activation.Activatable`

>     public class MyPersistentClass extends Activatable
>         implements examples.activation.YetAnotherRemoteInterface {

> Step 3:
> Declare a two-argument constructor in the implementation class

> In the constructor, in addition to the normal call to the superclass's constructor, in this example the `MarshalledObject` is used to specify the file name of the persistent data store. If the file exists, it's used to initialize this object's variable, a `Vector` named "`transactions`". If the file object doesn't exist, then the vector is manually initialized. If there is any error reading the file, then object construction fails.

>     private Vector transactions;
>     private File holder;

>     public MyPersistentClass(ActivationID id, MarshalledObject data)
>         throws RemoteException, ClassNotFoundException, java.io.IOException {

>         // Register the object with the activation system
>         // then export it on an anonymous port
>         super(id, 0);

>         // Extract the File object from the MarshalledObject that was
>         // passed to the constructor
>         //
>         holder = (File)data.get();

>         if (holder.exists()) {
>             // Use the MarshalledObject to restore my state
>     	//
>     	this.restoreState();
>         } else {
>     	transactions = new Vector(1,1);
>     	transactions.addElement("Initializing transaction vector");
>         }

>     }

> Step 4:
> Write the methods that use the `MarshalledObject`, to save and restore the object's data state

>     // If the MarshalledObject that was passed to the constructor was
>     // a file, then use it to recover the vector of transaction data
>     //
>     private void restoreState() throws IOException, ClassNotFoundException {
>         File f = holder;
>         FileInputStream fis = new FileInputStream(f);
>         ObjectInputStream ois = new ObjectInputStream(fis);
>         transactions = (Vector)ois.readObject();
>         ois.close();
>     }

>     private void saveState() {
>         try {
>     	File f  = holder;
>     	FileOutputStream fos = new FileOutputStream(f);
>     	ObjectOutputStream oos = new ObjectOutputStream(fos);
>     	oos.writeObject(getTransactions());
>     	oos.close();
>         } catch (Exception e) {
>     	throw new RuntimeException("Error saving vector of data");
>         }
>     }

> Step 5:
> Implement the remote interface method(s)

> Add each of the vector elements passed from the client to the object instance and save the updated vector out to a file.

>     public Vector calltheServer(Vector v) throws RemoteException {

>          int limit = v.size();
>          for (int i = 0; i < limit; i++) {
>              transactions.addElement(v.elementAt(i));
>          }

>          // Save this object's data out to file
>          //
>          this.saveState();
>          return transactions;
>     }

Creating the "setup" class

The job of the "setup" class is to create all the information necessary for the activatable class, without necessarily creating an instance of the remote object. For this example, the setup class will be `examples.activation.Setup4`.

The setup class passes information about the activatable class to `rmid`, registers a remote reference (an instance of the activatable class's stub class) and an identifier (name) with the `rmiregistry`, and then the setup class may exit. There are seven steps to create a setup class:

1. Make the appropriate imports
2. Install a `SecurityManager`
3. Create an `ActivationGroup` instance
4. Create an `ActivationDesc` instance
5. Remove the reference to the implementation class creation, and register with `rmid`
6. Bind the stub to a name in the `rmiregistry`
7. Quit the setup application

> Step 1:
> Make the appropriate imports in the setup class

>     import java.io.File;
>     import java.rmi.*;
>     import java.rmi.activation.*;
>     import java.util.Properties;

> Step 2:
> Install a `SecurityManager`

>     System.setSecurityManager(new RMISecurityManager());

> Step 3:
> Create an `ActivationGroup` instance

> Note: In this example, for simplicity, we will use a policy file that gives global permission to anyone from anywhere. Do not use this policy file in a production environment. For more information on how to properly open up permissions using a `java.security.policy` file, please refer to to the following documents:

> `Default Policy Implementation and Policy File Syntax`

> `Permissions in the Java 2 SDK`

> In the setup application, the job of the activation group descriptor is to provide all the information that `rmid` will require to contact the appropriate existing JavaTM virtual machine* (JVM) or spawn a new JVM for the activatable object.

> Note: In order to run this code on your system, you'll need to change the policy file location to be the absolute path to where you've installed the example policy file that came with the source code.

>     // Because of the Java 2 security model, a security policy should
>     // be specified for the ActivationGroup VM. The first argument
>     // to the Properties put method, inherited from Hashtable, is
>     // the key and the second is the value
>     //
>     Properties props = new Properties();
>     props.put("java.security.policy",
>        "/home/rmi_tutorial/activation/policy");

>     ActivationGroupDesc.CommandEnvironment ace = null;
>     ActivationGroupDesc exampleGroup = new ActivationGroupDesc(props, ace);

>     // Once the ActivationGroupDesc has been created, register it
>     // with the activation system to obtain its ID
>     //
>     ActivationGroupID agi =
>        ActivationGroup.getSystem().registerGroup(exampleGroup);

> Step 4:
> Create an `ActivationDesc` instance

> In the setup application, the job of the activation descriptor is to provide all the information that `rmid` will require to create a new instance of the implementation class.

> Note: In order to run this code on your system, you'll need to change the file URL location to be the location of the directory on your system, where you've installed the example source code.

>     // Don't forget the trailing slash at the end of the URL
>     // or your classes won't be found
>     //
>     String location = "file:/home/rmi_tutorial/activation/";

>     // Pass the file that we want to persist to as the Marshalled
>     // object
>     MarshalledObject data =  new MarshalledObject (new File(
>         "/home/rmi_tutorial/activation/persistentObjectStore.ser"));

>     // The location argument to the ActivationDesc constructor will be used
>     // to uniquely identify this class; it's location is relative to the
>     // URL-formatted String, location.
>     //
>     ActivationDesc desc = new ActivationDesc
>         (agi, "examples.activation.ActivatableImplementation",
>           location, data);

> Step 5:
> Declare an instance of your remote interface and register the activation descriptor with `rmid`

>     YetAnotherRemoteInterface yari =
>         (YetAnotherRemoteInterface)Activatable.register(desc);
>     System.out.println("Got the stub for MyPersistentClass");

> Step 6:
> Bind the stub, that was returned by the `Activatable.register` method, to a name in the `rmiregistry`

>     Naming.rebind("MyPersistentClass", yari);
>     System.out.println("Exported MyPersistentClass");

> Step 7:
> Quit the setup application

>     System.exit(0);

Compile and run the code

There are six steps to compile and run the code:

1. Compile the remote interface, implementation, client and setup classes
2. Run `rmic` on the implementation class
3. Start the `rmiregistry`
4. Start the activation daemon, `rmid`
5. Run the setup program
6. Run the client

> Step 1:
> Compile the remote interface, implementation, client and setup classes

>     % javac -d . YetAnotherRemoteInterface.java
>     % javac -d . MyPersistentClass.java
>     % javac -d . Client4.java
>     % javac -d . Setup4.java

> Step 2:
> Run `rmic` on the implementation class

>     % rmic -d . examples.activation.MyPersistentClass

> Step 3:
> Start the`rmiregistry`

> `% rmiregistry &`

> Note: Before you start the rmiregistry, you must make sure that the shell or window in which you will run the `registry`, either has no CLASSPATH set or has a CLASSPATH that does not include the path to any classes that you want downloaded to your client, including the stubs for your remote object implementation classes.

> If you start the `rmiregistry`, and it can find your stub classes in its CLASSPATH, it will ignore the server's `java.rmi.server.codebase` property, and as a result, your client(s) will not be able to download the stub code for your remote object.

> Step 4:
> Start the activation daemon,`rmid`

>     % rmid -J-Djava.security.policy=rmid.policy &

> Where `rmid.policy` is the name of the security policy file for `rmid`.

> Note: By default, `rmid` now requires a security policy file, that is used to verify whether or not the information in each `ActivationGroupDescriptor` is allowed to be used to launch a JVM for an activation group. For complete details, please refer to the `rmid` man page for the Solaris operating environment and the `rmid` man page for the Microsoft Windows platform.

> Step 5:
> Run the setup program

> Run the setup, setting the codebase property to be the location of the implementation stubs. There are four things that need to go on the same command line:

> 1. The "`java`" command
> 2. A property name=value pair that specifies the location of the security policy file
> 3. A property to specify where the stub code lives (no spaces from the "-D" all the way though the last "/")
> 4. The fully-qualified package name of the setup program.
> There should be one space just after the word "`java`", one between the two properties, and a third one just before the word "`examples`" (which is very hard to see when you view this as text, in a browser, or on paper).

> `% java -Djava.security.policy=/home/rmi_tutorial/activation/policy -Djava.rmi.server.codebase=file:/home/rmi_tutorial/activation/ examples.activation.Setup4`

> The codebase property will be resolved to a URL, so it must have the form of "`http://aHost/somesource/"` or "`file:/myDirectory/location/`" or, due to the requirements of some operating systems, "`file:///myDirectory/location/`" (three slashes after the "`file:`").

> While a `file:` URL is sometimes easier to use for running example code, using the `file:` URL will mean that the only clients that will be able to access the server are those that can access the same files system as the server (either by virtue of running on the same machine as the server or by using a shared filesystem, such as NFS). If you wish to use an HTTP server, but don't have one available to you, please feel free to download our HTTP server.

> Please note that each of these sample URL strings has a trailing "/". The trailing slash is a requirement for the URL set by the `java.rmi.server.codebase` property, so the implementation can resolve (find) your class definition(s) properly. For more information on setting the `java.rmi.server.codebase` property from the command line, please take a look at our tutorial on dynamic code downloading using the `java.rmi.server.codebase` property.

> If you forget the trailing slash on the property, or if the class files can't be located at the source (they aren't really being made available for download) or if you misspell the property name, you'll get thrown a `java.lang.ClassNotFoundException`. This exception will be thrown when you try to bind your remote object to the `rmiregistry`, or when the first client attempts to access that object's stub. If the latter case occurs, you have another problem as well because the `rmiregistry` was finding the stubs in its CLASSPATH.

> The server output should look like this:

>          Got the stub for MyPersistentClass
>          Exported MyPersistentClass

> Step 6:
> Run the client

> The argument to the client program is the hostname of the implementation server, in this case, "`vector`".

>     % java -Djava.security.policy=/home/rmi_tutorial/activation/policy
>     examples.activation.Client4 vector

> The first time that the client is run against this implementation, the output should look like this:

>     	Got a remote reference to the class MyPersistentClass
>             Called the remote method
>     	Result:
>     	Initializing transaction vector
>     	Deposited money
>     	Withdrew money
>     	Transferred money from Savings
>     	Check cleared
>     	Point-of-sale charge at grocery store

> The second time that the client is run against this implementation, the output will include five additional "transactions", so it should look like this:

>             Got a remote reference to the class MyPersistentClass
>     	Called the remote method
>     	Result:
>     	Initializing transaction vector
>     	Deposited money
>     	Withdrew money
>     	Transferred money from Savings
>     	Check cleared
>     	Point-of-sale charge at grocery store
>     	Deposited money
>     	Withdrew money
>     	Transferred money from Savings
>     	Check cleared
>     	Point-of-sale charge at grocery store

> Additionally, you should see the size of the `persistentObjectStore.ser` file increase, with each subsequent client call. *As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.

> Copyright © 1999 Sun Microsystems, Inc. All Rights Reserved. Please send comments to: rmi-comments@java.sun.com |